package com.android.support.lib.universal.utils;

import android.app.Activity;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.NinePatch;
import android.graphics.Rect;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * Created by Administrator on 2017/3/6.
 */

public class UtilAssets {
    private Context activity;//尽量使用Activity(兼容性比context好)
    private AssetManager am;
    private static UtilAssets assets;

    private UtilAssets(Context activity) {
        this.activity = activity;
        am = activity.getResources().getAssets();
        map = new HashMap<>();
        mapViews = new HashMap<>();
    }

    //初始化(兼容了Context和Activity,以Activity为主)
    public static UtilAssets getInstance(Activity activity) {
        if (activity != null && assets == null) {
            assets = new UtilAssets(activity);
        } else {
            if (activity != null && (!(assets.activity instanceof Activity))) {
                assets = new UtilAssets(activity);
            }
        }
        return assets;
    }

    //初始化
    public static UtilAssets getInstance(Context activity) {
        if (assets == null) {
            assets = new UtilAssets(activity);
        }
        return assets;
    }

    //位图数组(java对象赋值是传引用，都指向同一个对象)
    private HashMap<String, Bitmaps> map;
    //保存每一个View，防止View重复适配
    private HashMap<String, View> mapViews;

    class Bitmaps {
        String className;//标志
        Bitmap bitmap;

        public Bitmaps(String className, Bitmap bitmap) {
            this.className = className;
            this.bitmap = bitmap;
        }
    }

    BitmapFactory.Options options;

    public BitmapFactory.Options getOptions() {
        if (options != null) {
            return options;
        }
        //bitmap所占内存大小计算方式：图片长度 x 图片宽度 x 一个像素点占用的字节数（bitmap占用内存大小和图片本身大小无关）
        options = new BitmapFactory.Options();
        options.inPurgeable = true;//这个是关键。使用之后，基本不吃内存(内存不足是允许系统自动回收)
        options.inInputShareable = true;//和inPurgeable一起使用才有效。
        //其实如果不需要 alpha 通道，特别是资源本身为 jpg 格式的情况下，用这个格式RGB_565比较理想。
        options.inPreferredConfig = Bitmap.Config.RGB_565;//ARGB8888格式的图片(默认)，每像素占用 4 Byte，而 RGB565则是 2 Byte。内存可以直接缩小一半
//        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
        //options.inSampleSize = 2;//如果采样率为 2，那么读出来的图片只有原始图片的 1/4 大小。即长宽缩小一半,10就是缩小到原来的10分之1。一般不使用。图片质量会缩水。
        // options.inBitmap=inBitmap;//重用该bitmap的内存。节省内存。两个bitmap的长度和宽度必须一致才有效。才能重用。尽量不要使用。(会报错Problem decoding into existing bitmap)
        return options;
    }

    //设置位图，如("文件夹/文件名.后缀"),如果在assets文件夹共目录下直接写文件名即可。assets支持中文文件夹，如:"中文/nicks2.png"
    //从asstes里面加载图片和从mipmap-nodpi里面加载图片占用内存是一样的。主要还是要看Bitmap的优化。布局文件尽量不要直接引用mipmap-nodpi里的图片。没有对内存进行优化，很占内存。
    //以下方式是最省内存的加载Bitmap方法。
    public Bitmap getBitmapFromAssets(String claassName, String fileName, int resID) {
        String key;
        if (fileName != null && !fileName.equals("")) {
            key = fileName;
        } else {
            key = "" + resID;
        }
        //Log.e("test","key：\t"+key);
        if (map.get(key) != null && map.get(key).bitmap != null && !map.get(key).bitmap.isRecycled()) {//确保Bitmap不为null
            return map.get(key).bitmap;//防止重复加载，浪费内存
        }
        Bitmap bitmap = null;
        try {
//            Log.e("ui", "开始新建");
            if (fileName != null) {
                InputStream is = am.open(fileName);
                bitmap = BitmapFactory.decodeStream(is, null, getOptions());
                //byte[] b = UtilConnBitimap.InputStreamTOByte(is);
                //bitmap = BitmapFactory.decodeByteArray(b, 0, b.length, getOptions());//使用字节比使用流更省内存。
                //b = null;
                is.close();
                is = null;

            } else {
                bitmap = BitmapFactory.decodeResource(activity.getResources(), resID, getOptions());
            }
//            Log.e("ui", "大小:\t" + bitmap.getByteCount() / 1024 + "KB" + "\t宽度:\t" + bitmap.getWidth() + "\t高度:\t" + bitmap.getHeight() + "\tconfig:\t" + bitmap.getConfig());
            //保存当前Bitmap
            map.put(key, new Bitmaps(claassName, bitmap));
        } catch (Exception e) {
            e.printStackTrace();
//            Log.e("ui", "assets流异常" + e.getMessage());
        }
        return bitmap;
    }

    /**
     * 这样获取的九文件，基本不占内存
     *
     * @param resID drawable-nodpi文件夹下的九文件ID
     * @return NinePatchDrawable九文件，无则返回null
     */
    public NinePatchDrawable getNinePatchDrawable(int resID) {
        //九文件必须放在drawable-nodpi等系统文件夹下才有效。放在assets里是没有伸拉效果的，切记！
        Bitmap bitmap = getBitmapFromAssets(getClass().getName(), null, resID);
        //确认Bitmap是合法的NinePatch文件
        if (NinePatch.isNinePatchChunk(bitmap.getNinePatchChunk())) {
            //Log.e("test","我是合法九文件");
            NinePatchDrawable ninePatchDrawable = new NinePatchDrawable(activity.getResources(), bitmap, bitmap.getNinePatchChunk(), new Rect(), null);
            return ninePatchDrawable;
        } else {
            bitmap.recycle();
            bitmap = null;
            System.gc();
            return null;
        }
    }

    /**
     * 设置背景图片(控件选中样式，自己手动调用该方法。选中监听事件没有)
     *
     * @param clazz    getClass()对象
     * @param view     控件
     * @param fileName assets文件夹下背景图片名称
     * @param resID    如果fileName为null，才有效。
     */
    public void setBackGraound(final Class clazz, final View view, final String fileName, final int resID) {
        setBackGraound(clazz, view, fileName, resID, 0, 0, false);
    }

    /**
     * @param isRepeatAdapter 适配强制重新适配，true每次都重新适配。false只适配一次【默认就是false】
     */
    public void setBackGraound(final Class clazz, final View view, final String fileName, final int resID, boolean isRepeatAdapter) {
        setBackGraound(clazz, view, fileName, resID, 0, 0, isRepeatAdapter);
    }

    public void setBackGraound(final Class clazz, final View view, final String fileName, final int resID, int width, int heigh, boolean isRepeatAdapter) {
        if (view == null) {
            return;
        }
        //不要再线程中加载，会延迟。
        Bitmap bitmap = getBitmapFromAssets(clazz.getName(), fileName, resID);
        BitmapDrawable bitmapDrawable = new BitmapDrawable(bitmap);
        view.setBackgroundDrawable(bitmapDrawable);
        adapterView(bitmap, view, width, heigh, isRepeatAdapter);
        bitmapDrawable = null;
    }

    //针对ImageView的Src
    public void setImageBitmap(final Class clazz, final ImageView view, final String fileName, final int resID, int width, int heigh) {
        if (view == null) {
            return;
        }
        //不要再线程中加载，会延迟。
        Bitmap bitmap = getBitmapFromAssets(clazz.getName(), fileName, resID);
        view.setImageBitmap(bitmap);
        adapterView(bitmap, view, width, heigh, false);
    }

    //UI适配
    private void adapterView(Bitmap bitmap, final View view, int width, int heigh, boolean isRepeatAdapter) {

        if (!isRepeatAdapter && mapViews.containsKey(view.hashCode() + "") && mapViews.get(view.hashCode() + "").equals(view)) {
//            Log.e("test", "重复适配:\t" + view.hashCode());
            return;//重复适配。防止控件重复适配，浪费时间
        }

        mapViews.put(view.hashCode() + "", view);
//        Log.e("test", "适配:\t" + view.hashCode());
        //控件大小与图片大小一致
        ViewGroup.LayoutParams laParams = view
                .getLayoutParams();
        if (width <= 0) {
            laParams.width = bitmap.getWidth();
        } else {
            laParams.width = width;//自定义宽度
        }
        if (heigh <= 0) {
            laParams.height = bitmap.getHeight();//自定义高度
        } else {
            laParams.height = heigh;
        }
        view.setLayoutParams(laParams);
        //屏幕适配(调用UtilAssets就不需要再调用UtilProportion),只要对文字没有适配，基本都可以使用adapterView()
        UtilProportion.getInstance((Activity) activity).adapterView(view);
        bitmap = null;
        System.gc();
    }

    private View.OnFocusChangeListener oldOnFocusChangeListener;//记录旧焦点的聚焦事件,防止旧焦点失灵。

    /**
     * 设置聚焦图片
     *
     * @param clazz     当前 getClass()
     * @param view      控件(所有的控件包括布局都继承View)
     * @param falseName 非聚焦图片名称(正常图片)
     * @param trueName  聚焦图片名称
     * @param falseID   非聚焦图片ID,falseName为null才有效
     * @param trueID    聚焦图片ID,trueName为null才有效
     * @param Bfaouse   是否聚焦
     */
    public void setOnFocusChanged(final Class clazz, final View view, final String falseName, final String trueName, final int falseID, final int trueID, Boolean Bfaouse) {
        view.setFocusable(true);//是否具备聚焦能力
        if (Bfaouse) {
            view.requestFocus();
            setBackGraound(clazz, view, trueName, trueID);
        } else {
            setBackGraound(clazz, view, falseName, falseID);//正常样式
        }
        //保存原有的聚焦事件,失去焦点时要恢复原有聚焦事件
        oldOnFocusChangeListener = view.getOnFocusChangeListener();
        //Log.e("ui", "聚焦变化初始化:\t" + view.getId());
        view.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                //Log.e("ui", "聚焦:" + hasFocus);
                if (hasFocus) {
                    setBackGraound(clazz, view, trueName, trueID);
                } else {
                    //Log.e("test","失去焦点：:\t"+falseID);
                    setBackGraound(clazz, view, falseName, falseID);
                }
                //集成原有聚焦事件，防止原有聚焦事件失灵。
                if (oldOnFocusChangeListener != null) {
                    oldOnFocusChangeListener.onFocusChange(v, hasFocus);
                }
            }
        });
    }

    /**
     * 设置按下图片样式(手指按下不会聚焦，聚焦和手指是否按下没有直接影响)
     *
     * @param clazz      当前 getClass()
     * @param view       控件(所有的控件包括布局都继承View)
     * @param gennerName 手指离开图片样式(一般样式)
     * @param pressName  手机按下图片样式
     * @param gennerID   手指离开图片资源ID,gennerName为null有效
     * @param pressID    手指按下图片ID,pressName为null有效
     */
    public void setOnTouch(final Class clazz, final View view, final String gennerName, final String pressName, final int gennerID, final int pressID) {
        setBackGraound(clazz, view, gennerName, gennerID);//正常样式
        view.setClickable(true);//是否具备点击能力,必须设置否则无效
        view.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                //Log.e("ui","事件:"+event.getAction());
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN://按下
//                        Log.e("ui", "按下");
                        setBackGraound(clazz, view, pressName, pressID);
                        break;
                    case MotionEvent.ACTION_UP://离开
//                        Log.e("ui", "离开");
                        setBackGraound(clazz, view, gennerName, gennerID);
                        break;
                    default:
                        break;
                }
                return false;
            }
        });
    }

    /**
     * 获取帧动画
     *
     * @param clazz    getClass()
     * @param view     控件
     * @param fileName 文件名。不包括数字标志和后缀。如"star_0.png",传 "star_"
     * @param size     帧动画个数。下标从0开始。size就是最后一个数。如"0,1,2,3",就传3
     * @param duration 帧动画时间，单位毫秒。1000等于一秒。
     * @param BAssets  true图片在asstes文件夹。false图片在mipmap文件夹。
     * @return
     */
    public AnimationDrawable getBackGraoundAAnimationDrawable(final Class clazz, final View view, String fileName, int size, int duration, Boolean BAssets) {
        AnimationDrawable anim = new AnimationDrawable();
        for (int i = 0; i <= size; i++) {
//            Log.e("ui", " " + (fileName + i));
            Bitmap bitmap = null;
            if (BAssets) {
                fileName = fileName + i + ".png";
                bitmap = getBitmapFromAssets(clazz.getName(), fileName, 0);//资源在asstes文件下
            } else {
                int id = activity.getResources().getIdentifier(fileName + i, "mipmap", activity.getPackageName());//图片资源在mipmap下面
                bitmap = getBitmapFromAssets(clazz.getName(), null, id);
            }

            Drawable drawable = new BitmapDrawable(bitmap);
            anim.addFrame(drawable, duration);
        }
        //anim.setOneShot(false);//是否只循环一次
        //view.setBackground(anim);
        view.setBackgroundDrawable(anim);
        return (AnimationDrawable) view.getBackground();
    }

    /**
     * 消毁，释放内存。必须在Activity销毁时(super.onDestroy()之前)手动调用一次
     *
     * @param Bdes     是否销毁所有Bitmap缓存。false则根据class名销毁当前Activity位图
     * @param clazz    Class对象(getClass())
     * @param activity 释放当前activity布局内存
     */
    public void onDestroyed(Boolean Bdes, Class clazz, Activity activity) {
//        Log.e("test","销毁");
        Iterator it = map.entrySet().iterator();
        ArrayList<String> list = new ArrayList<>();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String key = entry.getKey().toString();
            if (entry.getValue() == null) {
                list.add(key);//清除统计标志。否则不会调用适配
                continue;
            }
            Bitmaps bitmaps = (Bitmaps) entry.getValue();
            String className = bitmaps.className;
            if ((Bdes || (className.equals(clazz.getName()))) && bitmaps.bitmap != null && !bitmaps.bitmap.isRecycled()) {
                // 回收并且置为null
                bitmaps.className = null;
                bitmaps.bitmap.recycle();
                bitmaps.bitmap = null;
//                Log.e("test", "bitmap回收");
                list.add(key);
                key = null;
            }
            entry = null;
        }
        it = null;
        for (int i = 0; i < list.size(); i++) {
            map.remove(list.get(i));//一次
//            Log.e("ui", "移出");
        }
        list.clear();
        list = null;
        if (Bdes && activity != null) {
            map.clear();
            map = null;
            mapViews.clear();
            mapViews = null;
            am = null;
            assets = null;
        }
        if (activity != null) {
            try {
                activity.setContentView(new View(activity));
//                Log.e("ui","activity销毁");
            } catch (Exception E) {
//                Log.e("ui","asstes销毁异常"+E.getMessage());
            }
        }
        System.gc();//回收无用的对象,释放内存
    }
}
